package com.fm.mqtt;

import com.fm.entity.*;
import com.fm.service.*;
import com.fm.utils.HttpUtils;
import com.fm.utils.JSONUtils;
import com.fm.utils.JavaExecScriptUtil;
import com.fm.utils.SpringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.simp.SimpMessagingTemplate;

import java.util.*;

/**
 * 处理topic的工具
 * Created by Administrator on 2018/8/8 0008.
 */
public class TopicUtils {

    private ApplicationContext applicationContext = SpringUtils.getApplicationContext();
    private ProductService productService = applicationContext.getBean(ProductService.class);
    private EquipmentService equipmentService = applicationContext.getBean(EquipmentService.class);
    private ProductFunctionService productFunctionService = applicationContext.getBean(ProductFunctionService.class);
    private RuleService ruleService = applicationContext.getBean(RuleService.class);
    private RuleSqlService ruleSqlService = applicationContext.getBean(RuleSqlService.class);
    private RuleDestinationService ruleDestinationService = applicationContext.getBean(RuleDestinationService.class);
    private TopicLogService topicLogService = applicationContext.getBean(TopicLogService.class);
    private SimpMessagingTemplate template = applicationContext.getBean(SimpMessagingTemplate.class);

    /**
     * 登录操作
     * @param productKey
     * @param eqName
     * @param message
     */
    public void doLogin(String productKey, String eqName, String message) {
        //根据产品和设备名获取设备
        Equipment equipment = equipmentService.getEquipmentByName(eqName);
        //修改设备的状态
        equipment.setStatus(2);
        equipmentService.update(equipment);
    }

    /**
     * 属性上报
     * @param productKey
     * @param eqName
     * @param message
     */
    public void doPostProperty(String productKey, String eqName, String message) {
        System.out.print("属性上报！");
        //1.根据产品key获取产品
        Product product = productService.getProductByKey(productKey);
        //2.根据设备名获取设备
        Equipment equipment = equipmentService.getEquipmentByName(eqName);
        String topic = "/thing/event/property/post";
        try{
            ALink aLink = JSONUtils.toBean(message,ALink.class);
            handleRule(equipment.getId(), message, topic, aLink);
            writeLog(equipment.getId(), product.getId(), 2, 1, message);
        }catch (Exception e) {
            System.out.println("A-LINK格式不正确！");
            e.printStackTrace();
            writeLog(equipment.getId(), product.getId(), 2, 0, e.getMessage());
        }
    }

    /**
     * 事件上报
     * @param productKey
     * @param eqName
     * @param event
     * @param message
     */
    public void doPostEvent(String productKey, String eqName, String event, String message) {
        System.out.print("事件上报！");
        //1.根据产品key获取产品
        Product product = productService.getProductByKey(productKey);
        //2.根据设备名获取设备
        Equipment equipment = equipmentService.getEquipmentByName(eqName);
        String topic = "/thing/event/"+event+"/post";
        try{
            ALink aLink = JSONUtils.toBean(message,ALink.class);
            handleRule(equipment.getId(), message, topic, aLink);
            writeLog(equipment.getId(), product.getId(), 2, 1, message);
        }catch (Exception e) {
            System.out.println("A-LINK格式不正确！");
            e.printStackTrace();
            writeLog(equipment.getId(), product.getId(), 2, 0, e.getCause().getMessage());
        }
    }

    /**
     * 退出登录
     * @param productKey
     * @param eqName
     * @param message
     */
    public void doLogout(String productKey, String eqName, String message) {
        System.out.print("退出登录！");
        //根据产品和设备名获取设备
        Equipment equipment = equipmentService.getEquipmentByName(eqName);
        //修改设备的状态
        equipment.setStatus(1);
        equipmentService.update(equipment);
    }


    /**
     * 处理规则
     * @param eqId
     * @param message
     * @param topic
     * @param aLink
     * @throws Exception
     */
    private void handleRule(Integer eqId, String message, String topic, ALink aLink) throws Exception {
        //3.获取规则引擎
        List<Rule> rules = ruleService.selectByEquipment(eqId, topic);
        for (Rule rule : rules) {
            RuleSql sql = new RuleSql();
            sql.setRuleId(rule.getId());
            List<RuleSql> ruleSqls = ruleSqlService.selectByExample(sql);
            //判断是不是满足规则引擎的条件
            Boolean isPass = true;
            for (RuleSql ruleSql : ruleSqls) {
                String param = ruleSql.getParam();
                String targetValue = aLink.getParams().get(param);
                if (!checkRule(ruleSql,targetValue)){
                    isPass = false;
                    return;
                }
            }
            if(isPass) {//验证通过了
                String sendStr = "";
                ALink sendLink = null;
                //判断是否自定义输出条件
                if (rule.getIsCustom() == 0) {
                    //开始执行下面的操作
                    //过滤发送的字段名
                    sendLink = filterAlink(aLink,rule.getFields());
                    sendStr = JSONUtils.toJSONString(sendLink);
                }else {
                    String jsres = JavaExecScriptUtil.jsFunction(rule.getCode(), JSONUtils.toJSONString(aLink.getParams()));
                    Map<String, String> reMap = JSONUtils.toBean(jsres, Map.class);
                    sendLink = aLink;
                    sendLink.setParams(reMap);
                    sendStr = JSONUtils.toJSONString(sendLink);
                }
                RuleDestination destination = new RuleDestination();
//                destination.setEquipmentId(eqId);
                destination.setRuleId(rule.getId());
                List<RuleDestination> ruleDestinations = ruleDestinationService.selectByExample(destination);
                for(RuleDestination de : ruleDestinations) {
                    if (de.getType() == 1) {
                        //这里是发到另一个topic
                        MqttPushClient.getInstance().publish(eqId,de.getValue(),sendStr);
                    }else {
                        HttpUtils.doGet(de.getValue(), sendLink);
                    }
                }
            }
        }
    }


    /**
     * 对Alink进行过滤
     * @param link
     * @param filter
     * @return
     */
    private ALink filterAlink(ALink link, String filter) {
        Map<String, String> param = new HashMap<>();
        String filters [] = filter.split(",");
        for (String key : link.getParams().keySet()) {
            if (Arrays.asList(filters).contains(key)) {//满足过滤条件
                param.put(key, link.getParams().get(key));
            }
        }
        link.setParams(param);
        return link;
    }


    public Boolean checkRule(RuleSql sql, String param) {
        String value = sql.getValue();
        if (sql.getType()==3) value = sql.getMethodParam();

        switch (sql.getConnector()){
            case "=":
                return value.equals(param);
            case ">":
                if (getTypeValue(value,param,sql.getParamType())!=1)
                    return false;
                break;
            case "<":
                if (getTypeValue(value,param,sql.getParamType())!=3)
                    return false;
                break;
            case ">=":
                if (getTypeValue(value,param,sql.getParamType())==3)
                    return false;
                break;
            case "<=":
                if (getTypeValue(value,param,sql.getParamType())==1)
                    return false;
                break;
            case "in":
                return Arrays.asList(value.split(",")).contains(param);
            case "between":
                return betweenValue(value, param, sql.getType()) ==1;
            case "like":
                return param.indexOf(value)>-1;
        }


        return true;
    }

    private Integer getTypeValue(String value,String target, Integer type) {
        switch (type){
            case 1:
                if (Integer.parseInt(target)>Integer.parseInt(value))
                    return 1;
                else if (Integer.parseInt(target)==Integer.parseInt(value))
                    return 2;
                else
                    return 3;
            case 2:
                if (Float.parseFloat(target)>Float.parseFloat(value))
                    return 1;
                else if (Float.parseFloat(target)==Float.parseFloat(value))
                    return 2;
                else
                    return 3;
            case 3:
                if (Double.parseDouble(target)>Double.parseDouble(value))
                    return 1;
                else if (Double.parseDouble(target)==Double.parseDouble(value))
                    return 2;
                else
                    return 3;
            default:
                return 0;
        }
    }

    private Integer betweenValue(String value, String target, Integer type){
        String[] targets  = target.split(",");
        switch (type){
            case 1:
                Integer f = Integer.parseInt(targets[0]);
                Integer s = Integer.parseInt(targets[1]);
                Integer v = Integer.parseInt(value);
                if ((f>v && v>s)||f<v && v<s)
                    return 1;
                else
                    return 2;
            case 2:
                Float a = Float.parseFloat(targets[0]);
                Float b = Float.parseFloat(targets[1]);
                Float c = Float.parseFloat(value);
                if ((a>c && c>b)||a<c && c<b)
                    return 1;
                else
                    return 2;
            case 3:
                Double g = Double.parseDouble(targets[0]);
                Double h = Double.parseDouble(targets[1]);
                Double i = Double.parseDouble(value);
                if ((g>i && i>h)||g<i && i<h)
                    return 1;
                else
                    return 2;
            default:
                return 0;
        }
    }

    /**
     * 写日志
     * @param equipmentId
     * @param productId
     * @param type
     * @param status
     * @param content
     */
    public void writeLog(Integer equipmentId, Integer productId, Integer type, Integer status, String content) {
        TopicLog log = new TopicLog();
        log.setEquipmentId(equipmentId);
        log.setProductId(productId);
        log.setType(type);
        log.setStatus(status);
        log.setContent(content);
        log.setCreateTime(new Date());
        topicLogService.insert(log);
        template.convertAndSend("/topic/equipment/" + equipmentId, log);
    }
}
